--------------------------------------------------------------------------------
-- 
-- S3PICPROG - PIC programmer with S3 starter kit
-- Copyright (C) 2008 Olivier Ringot <oringot@gmail.com>
-- 
-- This program is free software; you can redistribute it and/or
-- modify it under the terms of the GNU General Public License
-- as published by the Free Software Foundation; either version 2
-- of the License, or (at your option) any later version.
-- 
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU General Public License for more details.
-- 
-- You should have received a copy of the GNU General Public License
-- along with this program; if not, write to the Free Software
-- Foundation, Inc., 51 Franklin Street, Fifth Floor, 
-- Boston, MA  02110-1301, USA.
-- 
--------------------------------------------------------------------------------
-- 
-- $Revision: $
-- $Date: $
-- $Source: $
-- $Log: $
--
--------------------------------------------------------------------------------
library std;
use std.textio.all;

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_textio.all;

library s3picprog_lib;
use s3picprog_lib.s3picprog_pkg.all;

entity testbench_s3picprog is
end entity testbench_s3picprog;

architecture testbench of testbench_s3picprog is

  signal osc50              : std_logic;
  signal resetn             : std_logic;
  signal uart_counter       : std_logic_vector(6 downto 0);
  signal uart_clken         : std_logic;
  signal uart_wen           : std_logic;
  signal uart_busy          : std_logic;
  signal uart_busy_1t       : std_logic;
  signal uart_valid         : std_logic;
  signal uart_wdata         : std_logic_vector( 7 downto 0);
  signal uart_rdata         : std_logic_vector( 7 downto 0);
  signal uart_h2t           : std_logic;
  signal uart_t2h           : std_logic;
  signal uart_req           : std_logic;
  signal uart_done          : std_logic;
  signal burst_count        : std_logic_vector( 4 downto 0);
  
  type t_uart_state is (s_idle,s_wait_sending_addr,
                        s_send_data0,s_wait_sending_data0,
                        s_send_data1,s_wait_sending_data1,
                        s_receive_data,
                        s_wait_ack_ok,s_done);
                        
  signal uart_state         : t_uart_state;
 
  signal reg_addr           : std_logic_vector( 7 downto 0);
  signal reg_data           : std_logic_vector(15 downto 0);
  signal check_uart_rdata   : std_logic_vector(15 downto 0);
 
  signal pbutton            : std_logic_vector( 3 downto 0);
  signal switch             : std_logic_vector( 7 downto 0);
  signal pic_mclr           : std_logic;
  signal pic_pgm            : std_logic;
  signal pic_pgc            : std_logic;
  signal pic_pgd            : std_logic;

 
  signal constant_zero      : std_logic_vector(31 downto 0) := (others=>'0');
  signal constant_one       : std_logic_vector(31 downto 0) := (others=>'1');
  signal wait_counter       : integer   := 0;
  signal end_simu           : std_logic := '0';

begin

  -- ---------------------------------------------------------------------------
  -- assignment
  -- ---------------------------------------------------------------------------
  pbutton(3 downto 1) <= (others=>'0');
  switch              <= (others=>'0');
  pic_pgd             <= 'L';

  -- ---------------------------------------------------------------------------
  -- clock generation
  -- ---------------------------------------------------------------------------
  p_clock_generation:process
  begin
  
    if end_simu='0' then
    
      osc50 <= '0';
      wait for 10 ns;
      osc50 <= '1';
      wait for 10 ns;
      
    else
    
      wait;
        
    end if;
    
  end process p_clock_generation;
 
  -- ---------------------------------------------------------------------------
  -- UART clock enable 2.170138us
  -- ---------------------------------------------------------------------------
  p_uart_clken_generation:process(resetn,osc50)
  begin
  
    if resetn='0' then
    
      uart_clken   <= '0';
      uart_counter <= (others=>'0');
    
    elsif osc50'event and osc50='1' then
    
      uart_clken <= '0';

      if uart_counter="1101101" then -- 108
      
        uart_counter <= (others=>'0');
        uart_clken   <= '1';
      
      else
      
        uart_counter <= uart_counter + 1;
     
      end if;
    
    end if;
  
  end process p_uart_clken_generation;
  
  -- ---------------------------------------------------------------------------
  -- reset generation
  -- ---------------------------------------------------------------------------
  p_reset_generation:process
  begin
  
    resetn <= '0';
    wait for 100 us;
    resetn <= '1';
    wait;
    
  end process p_reset_generation;
  
  pbutton(0) <= not resetn;
  
  -- ---------------------------------------------------------------------------
  -- stimuli generation
  -- ---------------------------------------------------------------------------
  p_stimuli_generation:process(resetn,osc50)
  
    file file_in     : text open read_mode is "s3picprog.txt";
    variable line_in : line;
    variable tmp_i   : integer;
    variable tmp_c   : character;
    variable tmp_v   : std_logic_vector(31 downto 0);
    
  begin
  
    if resetn='0' then
    
     uart_req     <= '0';
     reg_addr     <= (others=>'0');
     reg_data     <= (others=>'0');
     wait_counter <= 0;
    
    elsif osc50'event and osc50='1' then
    
      if wait_counter/=0 then
      
        wait_counter <= wait_counter - 1;
      
      elsif uart_req='1' then
      
        if uart_done='1' then
        
          uart_req <= '0';
        
        end if;
      
      else
      
        loop
      
          exit when endfile(file_in);
          readline(file_in,line_in);
          read(line_in,tmp_c);
          exit when tmp_c='U' or
                    tmp_c='W' or
                    tmp_c='E';
      
        end loop;
    
        if endfile(file_in) or end_simu='1' then
      
          end_simu <= '1';
          
        else
      
          case tmp_c is
          
            when 'U'=>
            
              hread(line_in,tmp_v(7 downto 0));
              reg_addr     <= tmp_v(7 downto 0);
              
              if tmp_v(7 downto 0)="00100000" or
                 tmp_v(7 downto 0)="00100001" 
              then
              
                hread(line_in,tmp_v( 7 downto 0));
                hread(line_in,tmp_v(15 downto 8));
                check_uart_rdata <= tmp_v(15 downto 0);
             
              else
             
                hread(line_in,tmp_v( 7 downto 0));
                hread(line_in,tmp_v(15 downto 8));
                reg_data     <= tmp_v(15 downto 0);
              
              end if;
       
              uart_req     <= '1';
              
            when 'W'=>
            
              read(line_in,tmp_i);
              wait_counter <= tmp_i;
            
            when 'E'=>
            
              end_simu <= '1';
            
            when others=>
            
              assert false
                report "error: unknown command "&tmp_c&" !"
                  severity failure;
          
          end case;
      
        end if;
        
      end if;
    
    end if;
  
  end process p_stimuli_generation;
  
  -- ---------------------------------------------------------------------------
  -- UART controller
  -- ---------------------------------------------------------------------------
  p_uart_controller:process(resetn,osc50)
 
    variable line_out : line;
 
  begin
  
    if resetn='0' then
    
      uart_wen      <= '0';
      uart_wdata    <= (others=>'0');
      uart_state    <= s_idle;
      uart_busy_1t  <= '0';
      uart_done     <= '0';
      burst_count   <= "00000";
    
    elsif osc50'event and osc50='1' then
    
      uart_wen     <= '0';
      uart_busy_1t <= uart_busy;
      uart_done    <= '0';
      
      case uart_state is
    
        when s_idle=>
        
          if uart_req='1' then
          
            uart_wdata <= reg_addr;
            uart_wen   <= '1';
            uart_state <= s_wait_sending_addr;
          
          end if;
            
        when s_wait_sending_addr=>
        
          if uart_busy_1t='1' and uart_busy='0' then
          
            if reg_addr="00100000" then
            
              burst_count <= "00000";
              uart_state <= s_receive_data;
            
            elsif reg_addr="00100001" then
            
              burst_count <= "11111";
              uart_state <= s_receive_data;
              
            else
            
              if reg_addr="00110010" then
              
                burst_count <= "11111";
              
              else
              
                burst_count <= "00001";
                
              end if;
              
              uart_state <= s_send_data0;
            
            end if;
          
          end if;
          
        when s_send_data0=>
        
          uart_wdata <= reg_data(7 downto 0);
          uart_wen   <= '1';
          uart_state <= s_wait_sending_data0;
                    
        when s_wait_sending_data0=>
        
          if uart_busy_1t='1' and uart_busy='0' then
          
            burst_count <= burst_count - 1;
            uart_state <= s_send_data1;
          
          end if;
     
        when s_send_data1=>
        
          uart_wdata <= reg_data(15 downto 8);
          uart_wen   <= '1';
          uart_state <= s_wait_sending_data1;
          
        
        when s_wait_sending_data1=>
        
          if uart_busy_1t='1' and uart_busy='0' then
          
            if burst_count="00000" then
            
              uart_state <= s_wait_ack_ok;
            
            else
          
              burst_count <= burst_count - 1;
              uart_state  <= s_send_data0;
         
            end if;        
          
          end if;
          
        when s_receive_data=>
        
          if uart_valid='1' then
          
            if check_uart_rdata(7 downto 0)/=uart_rdata then
            
              write(line_out,string'("error: unexpected received data !"));
              writeline(output,line_out);
              write(line_out,string'("      read = "));
              hwrite(line_out,uart_rdata);
              writeline(output,line_out);
              write(line_out,string'("  expected = "));
              hwrite(line_out,check_uart_rdata(7 downto 0));
              writeline(output,line_out);
            
            end if;
            
            if burst_count="00000" then
            
              uart_state <= s_wait_ack_ok;  
              
            else
            
              burst_count <= burst_count - 1;
              uart_state  <= s_receive_data;
            
            end if;
            
          end if;
          
        when s_wait_ack_ok=>
        
          if uart_valid='1' then
          
            if uart_rdata/="00000000" then
            
              write(line_out,string'("error: unexpected acknowledge code !"));
              writeline(output,line_out);
              write(line_out,string'("  code = "));
              hwrite(line_out,uart_rdata);
              writeline(output,line_out);
            
            end if;
            
            uart_done  <= '1';
            uart_state <= s_done;
          
          end if;
          
        when s_done=>
        
          if uart_req='0' then
          
            uart_state <= s_idle;
            
          end if;
          
      end case;
    
    end if;
  
  end process p_uart_controller;
  
  -- ---------------------------------------------------------------------------
  -- PIC model
  -- ---------------------------------------------------------------------------
  --p_pic_model:process(pic_pgc,pic_mclr)
  --begin
  --
  --  if pic_mclr='0' then
  --  
  --  
  -- elsif pic_pgc'event and pic_pgc='
  --
  --end process p_pic_model;
  
  -- ---------------------------------------------------------------------------
  -- S3PICPROG instance
  -- ---------------------------------------------------------------------------
  s3picprog_0:s3picprog
  port map
  (
    -- system
    osc50          => osc50,
    
    -- switch
    pbutton        => pbutton,
    switch         => switch,
    
    -- led
    led            => open,
    
    -- rs-232
    uart_t2h       => uart_t2h,
    uart_h2t       => uart_h2t,
    uart_t2p       => open,
    uart_p2t       => constant_one(0),
    
    -- PIC interface
    pic_mclr       => pic_mclr,
    pic_pgm        => pic_pgm,
    pic_pgc        => pic_pgc,
    pic_pgd        => pic_pgd,
   
    -- unused
    anode_s7       => open,  
    s7             => open,  
    --
    sram_csn       => open, 
    sram_ben       => open, 
    sram_oen       => open, 
    sram_wen       => open, 
    sram_a         => open, 
    sram_dq        => open 
  );
  
  -- ---------------------------------------------------------------------------
  -- UART instance
  -- ---------------------------------------------------------------------------
  uart_0:uart
  port map
  (
    -- system
    resetn   => resetn,
    clk      => osc50,
    clken    => uart_clken,
    
    -- tx
    wen      => uart_wen,
    data_in  => uart_wdata,
    busy     => uart_busy,
    tx       => uart_h2t,
    
    -- rx
    rx       => uart_t2h,
    data_out => uart_rdata,
    valid    => uart_valid
  );

end architecture testbench;

library s3picprog_lib;

configuration rtl_conf of testbench_s3picprog is
  for testbench
    for all:s3picprog
      use entity s3picprog_lib.s3picprog(rtl);
    end for;  
    for all:uart
      use entity s3picprog_lib.uart(rtl);
    end for;  
  end for;
end configuration rtl_conf;

